package io.lvdaxian.mybatis18.session;


import io.lvdaxian.mybatis18.binding.MapperRegistry;
import io.lvdaxian.mybatis18.cache.Cache;
import io.lvdaxian.mybatis18.datasource.druid.DruidDataSourceFactory;
import io.lvdaxian.mybatis18.datasource.pooled.PooledDataSourceFactory;
import io.lvdaxian.mybatis18.datasource.unpooled.UnpooledDataSourceFactory;
import io.lvdaxian.mybatis18.executor.CachingExecutor;
import io.lvdaxian.mybatis18.executor.Executor;
import io.lvdaxian.mybatis18.executor.SimpleExecutor;
import io.lvdaxian.mybatis18.executor.keygen.KeyGenerator;
import io.lvdaxian.mybatis18.executor.parameter.ParameterHandler;
import io.lvdaxian.mybatis18.executor.resultset.DefaultResultSetHandler;
import io.lvdaxian.mybatis18.executor.resultset.ResultSetHandler;
import io.lvdaxian.mybatis18.executor.statement.PreparedStatementHandler;
import io.lvdaxian.mybatis18.executor.statement.StatementHandler;
import io.lvdaxian.mybatis18.mapping.BoundSql;
import io.lvdaxian.mybatis18.mapping.Environment;
import io.lvdaxian.mybatis18.mapping.MappedStatement;
import io.lvdaxian.mybatis18.mapping.ResultMap;
import io.lvdaxian.mybatis18.plugin.Interceptor;
import io.lvdaxian.mybatis18.plugin.InterceptorChain;
import io.lvdaxian.mybatis18.reflection.MetaObject;
import io.lvdaxian.mybatis18.reflection.factory.DefaultObjectFactory;
import io.lvdaxian.mybatis18.reflection.factory.ObjectFactory;
import io.lvdaxian.mybatis18.reflection.wrapper.DefaultObjectWrapperFactory;
import io.lvdaxian.mybatis18.reflection.wrapper.ObjectWrapperFactory;
import io.lvdaxian.mybatis18.scripting.LanguageDriver;
import io.lvdaxian.mybatis18.scripting.LanguageDriverRegistry;
import io.lvdaxian.mybatis18.scripting.xmltags.XMLLanguageDriver;
import io.lvdaxian.mybatis18.transaction.Transaction;
import io.lvdaxian.mybatis18.transaction.jdbc.JdbcTransactionFactory;
import io.lvdaxian.mybatis18.type.TypeAliasRegistry;
import io.lvdaxian.mybatis18.type.TypeHandlerRegistry;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

/**
 * 核心的配置类
 *
 * @author lihh
 */
public class Configuration {
  // 环境
  protected Environment environment;
  protected boolean useGeneratedKeys = false;
  // 缓存机制，默认不配置的情况是 SESSION
  protected LocalCacheScope localCacheScope = LocalCacheScope.SESSION;
  // 默认启用缓存，cacheEnabled = true/false
  protected boolean cacheEnabled = true;
  
  // mapper 注册实例
  protected final MapperRegistry mapperRegistry = new MapperRegistry(this);
  // mapped statement 语句集合
  protected final Map<String, MappedStatement> mappedStatements = new HashMap<>();
  // 缓存,存在Map里
  protected final Map<String, Cache> caches = new HashMap<>();
  // 类型别名注册
  protected final TypeAliasRegistry typeAliasRegistry = new TypeAliasRegistry();
  // 类型事件 注册
  protected final TypeHandlerRegistry typeHandlerRegistry = new TypeHandlerRegistry();
  protected final LanguageDriverRegistry languageRegistry = new LanguageDriverRegistry();
  
  // 对象工厂和对象包装器工厂
  protected ObjectFactory objectFactory = new DefaultObjectFactory();
  protected ObjectWrapperFactory objectWrapperFactory = new DefaultObjectWrapperFactory();
  
  // 结果映射，存在Map里
  protected final Map<String, ResultMap> resultMaps = new HashMap<>();
  protected final Map<String, KeyGenerator> keyGenerators = new HashMap<>();
  
  // 插件拦截器链
  protected final InterceptorChain interceptorChain = new InterceptorChain();
  
  protected String databaseId;
  
  public TypeHandlerRegistry getTypeHandlerRegistry() {
    return typeHandlerRegistry;
  }
  
  // 表示被加载过的资源
  private final Set<String> loadedResources = new HashSet<>();
  
  public Configuration() {
    // jdbc 事务工厂
    typeAliasRegistry.registerAlias("JDBC", JdbcTransactionFactory.class);
    // 徳鲁依 数据库连接
    typeAliasRegistry.registerAlias("DRUID", DruidDataSourceFactory.class);
    
    // 这里 数据源 注册别名
    typeAliasRegistry.registerAlias("UNPOOLED", UnpooledDataSourceFactory.class);
    typeAliasRegistry.registerAlias("POOLED", PooledDataSourceFactory.class);
    
    languageRegistry.setDefaultDriverClass(XMLLanguageDriver.class);
  }
  
  /**
   * 根据具体的 Class 添加 Mapper
   *
   * @param clazz 解析出的类
   * @param <T>   泛型
   * @author lihh
   */
  public <T> void addMapper(Class<T> clazz) {
    mapperRegistry.addMapper(clazz);
  }
  
  /**
   * 通过 类 以及 sqlSession 拿到 代理类
   *
   * @param clazz      接口类
   * @param sqlSession sqlSession
   * @return 返回代理类
   * @author lihh
   */
  public <T> T getMapper(Class<T> clazz, SqlSession sqlSession) {
    return mapperRegistry.getMapper(clazz, sqlSession);
  }
  
  /**
   * 从这个方法中 实例化出一个结果集处理器
   *
   * @param executor        执行器
   * @param mappedStatement mappedStatement 语句
   * @param boundSql        sql等
   * @return 返回结果集处理器
   * @author lihh
   */
  public ResultSetHandler newResultSetHandler(Executor executor, MappedStatement mappedStatement, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
    return new DefaultResultSetHandler(executor, mappedStatement, resultHandler, rowBounds, boundSql);
  }
  
  /**
   * 构建一个执行器
   *
   * @param transaction 传递的事务
   * @return 返回的执行器
   * @author lihh
   */
  public Executor newExecutor(Transaction transaction) {
    Executor executor = new SimpleExecutor(this, transaction);
    // 配置开启缓存，创建 CachingExecutor(默认就是有缓存)装饰者模式
    if (cacheEnabled) {
      executor = new CachingExecutor(executor);
    }
    return executor;
  }
  
  /**
   * 构建一个 statement handler
   *
   * @param executor        执行器
   * @param mappedStatement mapped statement语句
   * @param parameter       参数对象
   * @param resultHandler   结果处理器
   * @param boundSql        bound sql
   * @return 返回 statement handler
   * @author lihh
   */
  public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
    // 创建语句处理器，Mybatis 这里加了路由 STATEMENT、PREPARED、CALLABLE 我们默认只根据预处理进行实例化
    StatementHandler statementHandler = new PreparedStatementHandler(executor, mappedStatement, parameter, rowBounds, resultHandler, boundSql);
    
    // 嵌入插件，代理对象. 从这里执行插件
    statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
    return statementHandler;
  }
  
  /**
   * 通过 package 地址 添加 mappers
   *
   * @param packageName 包命令
   * @author lihh
   */
  public void addMappers(String packageName) {
    mapperRegistry.addMappers(packageName);
  }
  
  /**
   * 创建 metaObject 的实例
   *
   * @param object 传递的对象
   * @return 返回创建的 metaObject
   * @author lihh
   */
  public MetaObject newMetaObject(Object object) {
    return MetaObject.forObject(object, objectFactory, objectWrapperFactory);
  }
  
  public String getDatabaseId() {
    return databaseId;
  }
  
  /**
   * 判断 mapper 是否存在
   *
   * @param clazz 接口类
   * @author lihh
   */
  public boolean hasMapper(Class<?> clazz) {
    return mapperRegistry.hasMapper(clazz);
  }
  
  /**
   * 添加 mapper 对应的语句
   *
   * @param ms mapped 语句
   * @author lihh
   */
  public void addMappedStatement(MappedStatement ms) {
    mappedStatements.put(ms.getId(), ms);
  }
  
  /**
   * 判断 资源是否被加载
   *
   * @param resource 被加载的资源
   * @return true 被加载过 false 没有被加载过
   * @author lihh
   */
  public boolean isResourceLoaded(String resource) {
    return loadedResources.contains(resource);
  }
  
  /**
   * 添加被加载过的资源 用来做标识符
   *
   * @param resource 资源名称
   * @author lihh
   */
  public void addLoadedResource(String resource) {
    loadedResources.add(resource);
  }
  
  public MappedStatement getMappedStatement(String id) {
    return mappedStatements.get(id);
  }
  
  public TypeAliasRegistry getTypeAliasRegistry() {
    return typeAliasRegistry;
  }
  
  public Environment getEnvironment() {
    return environment;
  }
  
  public void setEnvironment(Environment environment) {
    this.environment = environment;
  }
  
  public LanguageDriverRegistry getLanguageRegistry() {
    return languageRegistry;
  }
  
  /**
   * 这里是 创建一个参数处理器
   *
   * @param mappedStatement mapped statement 语句
   * @param parameterObject params object
   * @param boundSql        绑定sql
   * @return 返回参数事件
   * @author lihh
   */
  public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
    ParameterHandler parameterHandler = mappedStatement.getLang().createParameterHandler(mappedStatement, parameterObject, boundSql);
    // todo 插件的一些参数，也是在这里处理，暂时不添加这部分内容 interceptorChain.pluginAll(parameterHandler);
    return parameterHandler;
  }
  
  /**
   * 获取 默认驱动的方式
   *
   * @return 返回的默认驱动
   * @author lihh
   */
  public LanguageDriver getDefaultScriptingLanguageInstance() {
    return languageRegistry.getDefaultDriver();
  }
  
  public ObjectFactory getObjectFactory() {
    return objectFactory;
  }
  
  public ResultMap getResultMap(String id) {
    return resultMaps.get(id);
  }
  
  public void addResultMap(ResultMap resultMap) {
    resultMaps.put(resultMap.getId(), resultMap);
  }
  
  public void addKeyGenerator(String id, KeyGenerator keyGenerator) {
    keyGenerators.put(id, keyGenerator);
  }
  
  public KeyGenerator getKeyGenerator(String id) {
    return keyGenerators.get(id);
  }
  
  public boolean hasKeyGenerator(String id) {
    return keyGenerators.containsKey(id);
  }
  
  public boolean isUseGeneratedKeys() {
    return useGeneratedKeys;
  }
  
  public void setUseGeneratedKeys(boolean useGeneratedKeys) {
    this.useGeneratedKeys = useGeneratedKeys;
  }
  
  public void addInterceptor(Interceptor interceptorInstance) {
    interceptorChain.addInterceptor(interceptorInstance);
  }
  
  public LocalCacheScope getLocalCacheScope() {
    return localCacheScope;
  }
  
  public void setLocalCacheScope(LocalCacheScope localCacheScope) {
    this.localCacheScope = localCacheScope;
  }
  
  public boolean isCacheEnabled() {
    return cacheEnabled;
  }
  
  public void setCacheEnabled(boolean cacheEnabled) {
    this.cacheEnabled = cacheEnabled;
  }
  
  public void addCache(Cache cache) {
    caches.put(cache.getId(), cache);
  }
  
  public Cache getCache(String id) {
    return caches.get(id);
  }
}
